/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.xml;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.constants.CodeConstants;
import org.zhiqim.kernel.constants.XmlConstants;
import org.zhiqim.kernel.util.Streams;

/**
 * Xml扩展工具类
 *
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
@AnAlias("Xmlx")
public class Xmlx implements XmlConstants, CodeConstants
{
    /**
     * 对XML值进行安全处理，包括&,>,<,",',©,®六项，以保证保存到XML中是正确的
     * 
     * @param value 原值
     * @return      安全处理后的值
     */
    public static String toXmlSave(String value)
    {
        value = value.replaceAll("&", "&amp;");
        //把&放在第一个处理，防止转换的&再被转换一次
        value = value.replaceAll(">", "&gt;");
        value = value.replaceAll("<", "&lt;");
        value = value.replaceAll("\"", "&quot;");
        value = value.replaceAll("\'", "&apos;");
        value = value.replaceAll("©", "&copy;");
        value = value.replaceAll("®", "&reg;");
        
        return value;
    }
    
    /**
     * 对XML值进行加载处理，安全处理的逆处理，包括&,>,<,",',©,®六项
     * @param value 原值
     * @return      安全处理后的值
     */
    public static String toXmlLoad(String value)
    {
        value = value.replaceAll("&amp;", "&");
        //把&放在第一个处理，防止有转换两次的&也支持
        value = value.replaceAll("&gt;", ">");
        value = value.replaceAll("&lt;", "<");
        value = value.replaceAll("&quot;", "\"");
        value = value.replaceAll("&apos;", "\'");
        value = value.replaceAll("&copy;", "©");
        value = value.replaceAll("&reg;", "®");
        return value;
    }
    
    /********************************************************/
    //以下XML开始和结束标志部分
    /********************************************************/
    
    /** 生成开始标记 */
    public static String toTagBegin(String tag)
    {
        return "<"+tag+">";
    }
    
    /** 生成结束标记 */
    public static String toTagEnd(String tag)
    {
        return "</"+tag+">";
    }
    
    /** 生成数据文本开始 */
    public static String toCDataBegin()
    {
        return _CDATA;
    }
    
    /** 生成数据文本结束 */
    public static String toCDataEnd()
    {
        return CDATA_;
    }
    
    /** 增加XML开始标记 */
    public static StringBuilder addTagBegin(StringBuilder strb, String tag)
    {
        return strb.append("<").append(tag).append(">");
    }
    
    /** 增加XML结束标记 */
    public static StringBuilder addTagEnd(StringBuilder strb, String tag)
    {
        return strb.append("</").append(tag).append(">");
    }
    
    /********************************************************/
    //以下XML标准Document相关
    /********************************************************/
    
    /** 获取一个XML空解析器，即不验证DOCTYPE内容 */
    public static EntityResolver getEmptyResolver()
    {
        return new EntityResolver()
        {
            @Override
            public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException
            {
                return new InputSource(new StringReader(_XML_DEFIND_));
            }
        };
    }
    
    /** 生成文档，对XML的DOCTYPE作检查 */
    public static Document buildDocument(String path, EntityResolver resolver) throws IOException
    {
        FileInputStream in = new FileInputStream(path);
        return buildDocument(in, resolver);
    }
    
    /** 生成文档，对XML的DOCTYPE不检查 */
    public static Document buildDocument(String path) throws IOException
    {
        FileInputStream in = new FileInputStream(path);
        return buildDocument(in, getEmptyResolver());
    }
    
    /** 生成文档，对XML的DOCTYPE不检查 */
    public static Document buildDocument(InputStream in) throws IOException
    {
        return buildDocument(in, getEmptyResolver());
    }
    
    /** 生成文档，对XML的DOCTYPE作检查 */
    public static Document buildDocument(InputStream in, EntityResolver resolver) throws IOException
    {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(false);
        factory.setIgnoringElementContentWhitespace(true);
        
        try
        {
            DocumentBuilder builder = factory.newDocumentBuilder();
            builder.setEntityResolver(resolver);
            return builder.parse(in);
        }
        catch(Exception e)
        {
            throw new IOException(e);
        }
    }
    
    /** 生成文档，并检查DOCTYPE，指定字符集 */
    public static Document buildDocument(String content, String charset, EntityResolver resolver) throws IOException
    {
        InputStream is = Streams.toStream(content, charset);
        
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(false);
        factory.setIgnoringElementContentWhitespace(true);
        
        try
        {
            DocumentBuilder builder = factory.newDocumentBuilder();
            builder.setEntityResolver(resolver);
            return builder.parse(is);
        }
        catch(Exception e)
        {
            throw new IOException(e);
        }
    }
    
    /** 生成文档，对XML的DOCTYPE不检查，指定字符集 */
    public static Document buildDocument(String content, String charset) throws IOException
    {
        return buildDocument(content, charset, getEmptyResolver());
    }
    
    /** 生成文档，对XML的DOCTYPE不检查，默认UTF-8 */
    public static Document buildDocumentUTF8(String content) throws IOException
    {
        return buildDocument(content, _UTF_8_);
    }
    
    /**
     * 判断是否节点和节点名称对应
     * 
     * @param node      节点
     * @param nodeName  要求的节点名称
     * @return          =true表示对应成功
     */
    public static boolean isOwn(Node node, String nodeName)
    {
        if (node == null || nodeName == null)
            return false;
        
        return nodeName.equals(node.getNodeName());
    }
    
    /**
     * 获取文档节点下的一个子节点
     * 
     * @param node      父节点
     * @param childName 子节点名称
     * @return          子节点，如果没有则返回null
     */
    public static Node getNode(Node node, String childName)
    {
        if (node == null || childName == null)
            return null;
        
        NodeList nodeList = node.getChildNodes();
        for (int i=0;i<nodeList.getLength();i++)
        {
            Node child = nodeList.item(i);
            if (childName.equals(child.getNodeName()))
                return child;
        }
        
        return null;
    }
    
    /**
     * 获取文档节点下的一个子节点
     * 
     * @param node      父节点
     * @param childName 子节点名称
     * @return          子节点，如果没有则返回null
     */
    public static List<Node> getNodeList(Node node, String childName)
    {
        if (node == null || childName == null)
            return new ArrayList<Node>();
        
        List<Node> list = new ArrayList<Node>();
        NodeList nodeList = node.getChildNodes();
        for (int i=0;i<nodeList.getLength();i++)
        {
            Node child = nodeList.item(i);
            if (childName.equals(child.getNodeName()))
                list.add(child);
        }
        
        return list;
    }
    
    /**
     * 指定属性名称表和名称，获取属性值
     * 
     * @param namedNodeMap  名称节点表   
     * @param name          名称
     * @return              属性值，没有对应的返回null
     */
    public static String getAttribute(NamedNodeMap namedNodeMap, String name)
    {
        return (namedNodeMap == null)?null:(namedNodeMap.getNamedItem(name) == null)?null:namedNodeMap.getNamedItem(name).getNodeValue();
    }
    
    /**
     * 指定节点和名称，获取属性值
     * 
     * @param node          节点
     * @param name          名称
     * @return              属性值，没有对应的返回null
     */
    public static String getAttribute(Node node, String name)
    {
        return (node == null)?null:(node.getAttributes().getNamedItem(name) == null)?null:node.getAttributes().getNamedItem(name).getNodeValue();
    }
}
